﻿using Accord;
using Accord.Imaging;
using Accord.Imaging.Filters;
using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.Linq;

namespace MDK.Device
{
    public class TubeTypeIdentifyFilter
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public int HueMin { get; set; }
        public int HueMax { get; set; }
        public float SaturationMin { get; set; }
        public float SaturationMax { get; set; }
        public float LightnessMin { get; set; }
        public float LightnessMax { get; set; }
        public int TubeMin { get; set; }
        public int TubeMax { get; set; }
    }

    public static class PalletBitmapHelper
    {
        private static FiltersSequence _targetFilterSeq;
        private static BlobCounter _targetBlobCounter;

        private static HSLFiltering[] _typeHslFilterings;

        private static Rectangle _activeRect;
        private static Rectangle _centerRect;
        private static int _centerThreshold;
        private static int _emptyThreshold;

        private static TubeTypeIdentifyFilter[] _typeFilters;

        public static Font ResultFont { get; set; } = new Font(SystemFonts.CaptionFont.FontFamily, 16, FontStyle.Bold);

        public static void Init(Rectangle activeRect, Rectangle centerRect, int centerThreshold, int emptyThreshold, TubeTypeIdentifyFilter[] filters)
        {
            _activeRect = activeRect;
            _centerRect = centerRect;
            _typeFilters = filters;
            _typeHslFilterings = filters.Select(t => new HSLFiltering(new IntRange(t.HueMin, t.HueMax),
                new Range(t.SaturationMin, t.SaturationMax),
                new Range(t.LightnessMin, t.LightnessMax))).ToArray();

            _targetBlobCounter = new BlobCounter()
            {
                FilterBlobs = true
            };

            _targetFilterSeq = new FiltersSequence()
            {
                new Grayscale(0.2125, 0.7154, 0.0721),
                new Threshold(10)
            };

            _centerThreshold = centerThreshold;
            _emptyThreshold = emptyThreshold;
        }

        public static TubeTypes[] IdentifyTubeTypes(this Bitmap palletBmp, out Bitmap result)
        {
            var types = IdentifyTubeTypesImpl(palletBmp, out result);
            GC.Collect();
            return types;
        }

        public static int IdentifyZirongxue(this Bitmap palletBmp)
        {
            return 0;
        }

        private static unsafe TubeTypes[] IdentifyTubeTypesImpl(this Bitmap palletBmp, out Bitmap result)
        {
            if (_activeRect.Width > palletBmp.Width || _activeRect.Height > palletBmp.Height)
            {
                result = palletBmp;
                return null;
            }

            TubeTypes[] types = Enumerable.Range(0, 20).Select(t => TubeTypes.Unknown).ToArray();

            // 初始化
            var actBmp = palletBmp.Clone(_activeRect, PixelFormat.Format24bppRgb);
            var actUMBmp = UnmanagedImage.FromManagedImage(actBmp);

            // 定位中心点
            var holePoints = new IntPoint[20];
            var offHolePoints = new IntPoint[20];
            var centerUMBmp = UnmanagedImage.FromManagedImage(palletBmp.Clone(_centerRect, PixelFormat.Format24bppRgb));
            centerUMBmp = new Grayscale(0.2125, 0.7154, 0.0721).Apply(centerUMBmp);
            centerUMBmp = new Threshold(_centerThreshold).Apply(centerUMBmp);
            var centerCounter = new BlobCounter()
            {
                FilterBlobs = true,
                MinWidth = 40,
                MaxWidth = 80,
                MinHeight = 40,
                MaxHeight = 80
            };
            centerCounter.ProcessImage(centerUMBmp);
            var centers = centerCounter.GetObjectsRectangles();
            var centerData = (byte*)centerUMBmp.ImageData;
            IntPoint center = default(IntPoint);
            Rectangle centerRect = default(Rectangle);
            foreach (var rect in centers)
            {
                var blackCount = 0;
                // 遍历圆
                for (int x = rect.X + rect.Width / 4; x < rect.X + rect.Width * 3 / 4; x++)
                {
                    for (int y = rect.Y + rect.Height / 4; y < rect.Y + rect.Height * 3 / 4; y++)
                    {
                        if (centerData[y * centerUMBmp.Stride + x] == 0)
                        {
                            blackCount++;
                        }
                    }
                }

                if (blackCount * 100 < rect.Width * rect.Height)
                {
                    center = new IntPoint(_centerRect.X - _activeRect.X + rect.X + rect.Width / 2, _centerRect.Y - _activeRect.Y + rect.Y + rect.Height / 2);
                    centerRect = new Rectangle(_centerRect.X - _activeRect.X + rect.X, _centerRect.Y - _activeRect.Y + rect.Y, rect.Width, rect.Height);
                    var hole = 0;
                    for (int k = 0; k < 25; k++)
                    {
                        if (k == 0 || k == 4 || k == 12 || k == 20 || k == 24) continue;
                        var cx = center.X + (2 - k / 5) * 130;
                        var cy = center.Y + (k % 5 - 2) * 130;
                        holePoints[hole] = new IntPoint(cx, cy);
                        offHolePoints[hole] = new IntPoint(cx + (2 - k / 5) * 20, cy + (k % 5 - 2) * 25);
                        hole++;
                    }

                    break;
                }
            }

            if (center == default(IntPoint))
            {
                result = actBmp;
                return types;
            }
            var actBmpG = Graphics.FromImage(actBmp);
            actBmpG.FillEllipse(Brushes.LimeGreen, centerRect);

            // 识别空位
            actUMBmp = new Grayscale(0.2125, 0.7154, 0.0721).Apply(actUMBmp);
            actUMBmp = new Threshold(_emptyThreshold).Apply(actUMBmp);
            actUMBmp = new Invert().Apply(actUMBmp);

            var emptyCounter = new BlobCounter()
            {
                FilterBlobs = true,
                MinWidth = 60,
                MaxWidth = 100,
                MinHeight = 60,
                MaxHeight = 100
            };
            emptyCounter.ProcessImage(actUMBmp);
            var emptys = emptyCounter.GetObjectsRectangles();
            var artData = (byte*)actUMBmp.ImageData;
            foreach (var rect in emptys)
            {
                for (int k = 0; k < types.Length; k++)
                {
                    if (types[k] >= 0) continue;
                    if (rect.Contains(holePoints[k].X, holePoints[k].Y))
                    {
                        var blackCount = 0;
                        // 遍历圆
                        for (int x = rect.X + rect.Width / 4; x < rect.X + rect.Width * 3 / 4; x++)
                        {
                            for (int y = rect.Y + rect.Height / 4; y < rect.Y + rect.Height * 3 / 4; y++)
                            {
                                if (artData[y * actUMBmp.Stride + x] == 0)
                                {
                                    blackCount++;
                                }
                            }
                        }
                        if (blackCount * 16 < rect.Width * rect.Height)
                        {
                            types[k] = 0;
                            actBmpG.DrawString($"{k + 1:D2}:空位", ResultFont, Brushes.Red, rect);
                            break;
                        }

                    }
                }
            }

            // 覆盖不需要检测的图片
            var paths = new GraphicsPath();
            for (int i = 0; i < types.Length; i++)
            {
                if (types[i] >= 0) continue;

                paths.AddEllipse(offHolePoints[i].X - 65, offHolePoints[i].Y - 65, 130, 130);
            }
            var middle = new Bitmap(actBmp.Width, actBmp.Height);
            var middleG = Graphics.FromImage(middle);
            middleG.Clear(Color.Black);
            middleG.SetClip(paths);
            middleG.DrawImage(actBmp, 0, 0);

            // 识别
            actUMBmp = UnmanagedImage.FromManagedImage(middle);
            for (var type = 0; type < _typeHslFilterings.Length; type++)
            {
                var target = _typeHslFilterings[type].Apply(actUMBmp);
                target = _targetFilterSeq.Apply(target);
                _targetBlobCounter.MinWidth = _targetBlobCounter.MinHeight = _typeFilters[type].TubeMin;
                _targetBlobCounter.MaxWidth = _targetBlobCounter.MaxHeight = _typeFilters[type].TubeMax;
                _targetBlobCounter.ProcessImage(target);
                var blobs = _targetBlobCounter.GetObjectsRectangles();
                foreach (var blob in blobs)
                {
                    for (int i = 0; i < holePoints.Length; i++)
                    {
                        if (types[i] >= 0) continue;

                        var rect = new Rectangle(offHolePoints[i].X - 65, offHolePoints[i].Y - 65, 130, 130);

                        if (rect.Contains(blob.X, blob.Y))
                        {
                            types[i] = (TubeTypes)_typeFilters[type].Id;
                            // 画类型
                            var text = $"{i + 1:D2}:{_typeFilters[type].Name}";
                            var textSize = actBmpG.MeasureString(text, ResultFont);
                            actBmpG.DrawString(text, ResultFont, Brushes.LimeGreen, rect.X + 65 - textSize.Width / 2, rect.Y + +65 - textSize.Height / 2);
                            break;
                        }
                    }
                }

                //                if (_typeFilters[type].Id == 2)
                //                {
                //                    target = new BlobsFiltering(_targetBlobCounter.MinWidth, _targetBlobCounter.MinHeight, _targetBlobCounter.MaxWidth, _targetBlobCounter.MaxHeight).Apply(target);
                //                    result = target.ToManagedImage();
                //                    return null;
                //                }
            }
            actBmpG.Dispose();

            result = actBmp;

            return types;
        }
    }
}
